package top.hjcwzx.wrench.limiter.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import top.hjcwzx.wrench.limiter.exception.WrenchKitLimterException;
import top.hjcwzx.wrench.limiter.annotation.KeyRateLimiter;
import top.hjcwzx.wrench.limiter.strategy.algorithm.RateLimiterAlgorithmExecutor;
import top.hjcwzx.wrench.limiter.strategy.key.RateLimitKeyExecutor;

import java.lang.reflect.Method;

@Aspect
public class KeyRateLimiterAspect extends AbstractCommonLimiter implements Ordered {

    Logger logger = LoggerFactory.getLogger(KeyRateLimiterAspect.class);

    @Autowired
    private RateLimiterAlgorithmExecutor algorithmExecutor;
    @Autowired
    private RateLimitKeyExecutor keyExecutor;

    @Pointcut("@annotation(top.hjcwzx.wrench.limiter.annotation.KeyRateLimiter)")
    public void aopPoint() {
    }

    @Around("aopPoint() && @annotation(rateLimiter)")
    public Object around(ProceedingJoinPoint pjp, KeyRateLimiter rateLimiter) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Object[] args = pjp.getArgs();


        // 1. 解析 key
        String rawKey = rateLimiter.key();
        String parsedKey = keyExecutor.getKey(rawKey, rateLimiter.keyStrategy(), method, args, pjp.getTarget());

        logger.debug("wrench-kit工具,key限流管理,parsedKey=" + parsedKey);

        // 2. 执行限流判断
        boolean allowed = algorithmExecutor.tryAccess(parsedKey, rateLimiter.permitsPerSecond(), rateLimiter.algorithmStrategy());

        if (!allowed) {
            if (!rateLimiter.fallbackMethod().isEmpty()) {
                try {
                    return commonFallBack(pjp, rateLimiter.fallbackBean(), rateLimiter.fallbackMethod());
                } catch (Exception ex) {
                    logger.error("fallback error", ex);
                    throw new WrenchKitLimterException("limit used，but fallback error");
                }
            }
            throw new WrenchKitLimterException("ip limit access");
        }

        return pjp.proceed();
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
